// ignore_for_file: import_of_legacy_library_into_null_safe

import 'dart:async';
import 'dart:math';
import 'dart:ui';
//import 'dart:convert';
//import 'dart:ffi';
//import 'dart:io';
//import 'dart:typed_data';
//import 'dart:ffi';
//import 'dart:math';

//import 'package:fft/fft.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:fl_chart/fl_chart.dart';

import 'temp/temp.dart';

late Database _db;

void main() async {
  // 打开或创建数据库
  sqfliteFfiInit();
  _db = await databaseFactoryFfi.openDatabase("cfg.db",
      options: OpenDatabaseOptions(
          version: 1,
          onCreate: (Database db, int version) async {
            await db.execute(
                '''CREATE TABLE sensor ( id TEXT PRIMARY KEY, hide NUMERIC, alarm NUMERIC, temp REAL, age NUMERIC, name TEXT, adj TEXT, energy NUMERIC)''');
            await db.execute(
                '''CREATE TABLE sensor_temp_his ( id TEXT, age NUMERIC, temp REAL, PRIMARY KEY (id, age))''');
          }));
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  final FlutterChartController myController = Get.put(FlutterChartController());

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '温度监视器',
      theme: ThemeData(
          primarySwatch: Colors.blue,
          textTheme: const TextTheme(
            bodySmall: TextStyle(
                fontSize: 12.0,
                color: Color.fromARGB(128, 255, 255, 255),
                fontFamily: "Microsoft YaHei"),
            bodyMedium: TextStyle(
                fontSize: 12.0,
                color: Color.fromARGB(128, 255, 255, 255),
                fontFamily: "Microsoft YaHei"),
            bodyLarge: TextStyle(
                fontSize: 12.0,
                color: Color.fromARGB(128, 255, 255, 255),
                fontFamily: "Microsoft YaHei"),
          ),
          elevatedButtonTheme: ElevatedButtonThemeData(
            style: ButtonStyle(
              textStyle: WidgetStateProperty.all<TextStyle>(const TextStyle(
                  fontSize: 13.0,
                  fontWeight: FontWeight.w600,
                  color: Color.fromARGB(226, 31, 52, 241),
                  fontFamily: "Microsoft YaHei")),
            ),
          )),
      home: GetBuilder<FlutterChartController>(
        //id:**,该id如何在update()函数数据里面存在的时候,这里的属性值才会跟着发生变化.默认不写的话,是包含的.
        init: myController,
        builder: (controller) {
          return const Main();
        },
      ),
    );
  }
}

class FlutterChartController extends GetxController {
  final BasicMessageChannel _serialData =
      const BasicMessageChannel("serialData", TempSensorCodec());
  final BasicMessageChannel _serial =
      const BasicMessageChannel("serial", Utf8StringCodec());
  final RxList<String> _devs = <String>[].obs;
  final Rx<String> _cur = "".obs;
  final RxList<FlSpot> _his = <FlSpot>[].obs;
  final Rx<String> _selId = "".obs;
  final Rx<String> _selLable = "".obs;
  final Rx<int> _selEnergy = 0.obs;
  final Rx<double> _selTemp = 0.0.obs;
  final RxList<TempSensor> _datas = <TempSensor>[].obs;
  final Map<String, TempSensor> _allDatas = Map.of(<String, TempSensor>{});
  final RxBool _hide = false.obs;
  String _hisId = "";

  @override
  void onInit() async {
    super.onInit();

    await _loadCfg();
    /*
    for (var i = 0; i < 10; i++) {
      var tmp = i;
      updateTempSensor(TempSensorData(
          "test-$i", "", "", tmp.toDouble(), tmp > 50, DateTime.now(), 3));
    }
    */

    _serial.setMessageHandler((message) async {
      var list = message?.split("\n") ?? [];
      _cur.value = list[0];
      _devs.value = list.sublist(1);

      update();
      return message;
    });
    _serialData.setMessageHandler(<TempSensorCodec>(message) async {
      if (message != null) updateTempSensor(message);
      return message;
    });

    ever(_selId, (newId) {
      print("sel: $newId");
      if (newId != _hisId) {
        _hisId = newId;
        _his.clear();
        print("clear-his: $newId");
        _loadHis(newId).then((value) {
          if (newId == _hisId) {
            _his.value = value;
            print("load-his[$newId]: ${value.join(", ")}");
            _his.refresh();
          }
        });
        _his.refresh();
      }
    });

  /*
    Timer.periodic(const Duration(seconds: 1), (timer) {
      //var tmp = Random.secure().nextDouble()*100;
      //updateTempSensor(TempSensorData("test-0", tmp, tmp>50, DateTime.now()));
      final id = _selId.value;
      if (id != "" && id != "null") {
        var tmp = Random.secure().nextDouble() * 20 + 20;
        updateTempSensor(
            TempSensorData(id, "", "", tmp, tmp > 50, DateTime.now(), 50));
      }
      _validShow();
    });
  */
  }

  Future<void> _loadCfg() async {
    await _db.query('sensor').then((rows) {
      for (var row in rows) {
        var sensor = TempSensor.map(row);
        _allDatas[sensor.data.id] = sensor;
      }
    });
    _validShow();
  }

  Future<List<FlSpot>> _loadHis(String id) async {
    return await _db
        .query('sensor_temp_his',
            where: 'id=?', whereArgs: [id], limit: 300, orderBy: 'age DESC')
        .then((rows) {
      Iterable<FlSpot> list =
          rows.map((m) => FlSpot(m['age']!.toDouble, m['temp']!.toDouble));
      List<FlSpot> result = [];
      for (var n in list) {
        result.insert(0, n);
      }
      return result;
    });
  }

  void _save(TempSensor data) {
    var d = data.toMap();
    _db.insert('sensor', d, conflictAlgorithm: ConflictAlgorithm.replace);
    _db.insert(
        'sensor_temp_his',
        <String, Object>{
          'id': d['id']!,
          'temp': d['temp']!,
          'age': d['age']!,
        },
        conflictAlgorithm: ConflictAlgorithm.replace);
  }

  void _delete(String id) {
    _allDatas.remove(id);
    _db.delete('sensor', where: 'id=?', whereArgs: [id]);
    _db.delete('sensor_temp_his', where: 'id=?', whereArgs: [id]);
  }

  void selShow(bool hide) {
    _hide.value = hide;
    _validShow();
  }

  bool updateTempSensor(TempSensorData data) {
    bool ok = true;
    if (_hisId == data.id && _hisId != "") {
      print(
          "his-add[$_hisId]: ${data.age.toInt.toDouble().toStringAsFixed(2)}, ${data.temp.toDouble().toStringAsFixed(2)}");
      _his.add(FlSpot(data.age.toInt.toDouble(), data.temp.toDouble()));
    }
    if (_allDatas.containsKey(data.id)) {
      ok = _allDatas[data.id]!.data.update(data);
    } else {
      _allDatas[data.id] = TempSensor(data);
    }
    if (ok) {
      print("update: ${_allDatas[data.id]!.data.toMap()}");
      _save(_allDatas[data.id]!);
    }
    _validShow();
    return ok;
  }

  void _validShow() {
    _datas.value =
        _allDatas.values.where((value) => value.hide == _hide.value).toList();
    final selId = _selId.value;
    final cur = _datas.firstWhere((n) => n.data.id == selId,
        orElse: () => TempSensor.empty());
    _selId.value = cur.data.id;
    _selLable.value = cur.data.label;
    _selTemp.value = cur.data.temp;
    _selEnergy.value = cur.data.energy;
  }
}

class Main extends GetView<FlutterChartController> {
  const Main({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(mainAxisAlignment: MainAxisAlignment.start, children: [
        DecoratedBox(
            decoration: BoxDecoration(
              color: Colors.grey[300], // 背景颜色
            ),
            child: Padding(
                padding:
                    const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
                child: Row(
                  mainAxisSize: MainAxisSize.max,
                  children: [
                    ElevatedButton(
                        onPressed: () {
                          controller._hide.value = false;
                          controller._validShow();
                        },
                        child: const Text("设备列表")),
                    const Spacer(),
                    ElevatedButton(
                        onPressed: () {
                          controller._hide.value = true;
                          controller._validShow();
                        },
                        child: const Text("配对列表")),
                    const Spacer(),
                    ElevatedButton(
                        onPressed: () {
                          selFromList(context, controller._devs)
                              .then(_selSerial);
                        },
                        child: const Text("选择串口")),
                    const Spacer(flex: 50),
                  ],
                ))),
        Expanded(
            child: Row(mainAxisSize: MainAxisSize.max, children: [
          Container(
            color: const Color.fromARGB(255, 63, 63, 63),
            width: 160,
            child: Obx(() => ListView.builder(
                  padding: const EdgeInsets.all(4.0),
                  itemCount: controller._datas.length,
                  itemBuilder: (BuildContext context, int index) {
                    return tempListBuilder(context, index, () {
                      if (index < controller._datas.length) {
                        controller._selId.value =
                            controller._datas[index].data.id;
                      } else {
                        controller._selId.value = "";
                      }
                    });
                  },
                )),
          ),
          Expanded(
            child: Container(
                color: const Color.fromARGB(255, 13, 25, 33),
                child: Obx(() => _makeDetailForTempData())),
          )
        ]))
      ]),
    );
  }

  Widget textField(
    String val,
    void Function(String, bool) onSubmitted, {
    String labelText = '',
    bool obscureText = false,
    TextInputType keyboardType = TextInputType.text,
  }) {
    final ctrl = TextEditingController.fromValue(TextEditingValue(
        text: val, //判断keyword是否为空
        selection: TextSelection.fromPosition(TextPosition(
            affinity: TextAffinity.downstream, offset: val.length))));
    return TextField(
      obscureText: obscureText,
      keyboardType: keyboardType,
      decoration: InputDecoration(
        labelText: labelText,
        isCollapsed: true,
        contentPadding:
            const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
        border: OutlineInputBorder(
            gapPadding: 0, borderRadius: BorderRadius.circular(5)),
      ),
      onChanged: (text) {
        ctrl.value.copyWith(
            text: text,
            selection: TextSelection.collapsed(offset: text.length));
        onSubmitted(text, false);
      },
      onEditingComplete: () {
        onSubmitted(ctrl.text, true);
      },
      controller: ctrl,
    );
  }

  void _selSerial(int index) {
    if (index < 0) return;
    controller._serial.send(controller._devs[index]);
  }

  Future<int> selFromList(BuildContext context, List<String>? list) async {
    Completer<int> c = Completer<int>();
    showModalBottomSheet(
        isScrollControlled: true,
        context: context,
        backgroundColor: Colors.transparent,
        builder: (BuildContext context) {
          return AnimatedPadding(
              // AnimatedPadding包裹，防止键盘挡住
              padding: MediaQuery.of(context).viewInsets,
              duration: const Duration(milliseconds: 100),
              child: SizedBox(
                child: ListView.builder(
                  itemBuilder: (context, index) {
                    return Padding(
                        padding: const EdgeInsets.symmetric(
                            vertical: 2, horizontal: 30),
                        child: OutlinedButton(
                            onPressed: () {
                              c.complete(index);
                              Navigator.of(context).pop(index);
                            },
                            style: ButtonStyle(
                                backgroundColor: const WidgetStatePropertyAll(
                                    Color.fromARGB(151, 255, 255, 255)),
                                padding: WidgetStateProperty.all(
                                    const EdgeInsets.symmetric(
                                        vertical: 20, horizontal: 5))),
                            child: Text(list![index])));
                  },
                  itemCount: list?.length ?? 0,
                  shrinkWrap: true,
                ),
              ));
        });
    return await c.future;
  }

  Widget? tempListBuilder(BuildContext context, int index, Function() onSel) {
    final s = controller._datas[index];
    final d = s.data;
    Color c = d.alarm ? Colors.red : Colors.blueGrey;
    Color tc = d.online ? Colors.white : Colors.white60;
    return ListTile(
        leading: Icon(Icons.access_alarm, color: c),
        title: Text(
          d.id,
          style: TextStyle(
              fontWeight: FontWeight.bold,
              fontSize: 20.0,
              color: tc,
              shadows: const [
                Shadow(
                    offset: Offset(1.0, 1.0),
                    color: Color.fromARGB(61, 0, 0, 0))
              ]),
        ),
        onTap: onSel);
  }

  Widget _makeDetailForTempData() {
    print("_makeDetailForTempData: ${controller._selId}");
    const ts = TextStyle(
        color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.bold);
    final hide = controller._hide;
    makeLable(double value) {
      if (controller._his.isEmpty) {
        return const Text("");
      }
      var range = controller._his.last.x - controller._his.first.x;
      String txt = DateTime.fromMicrosecondsSinceEpoch(value.toInt())
          .toLocal()
          .toString();
      if (range < 3600 * 1000 * 1000) {
        txt = txt.substring(11, 19);
      } else if (range < 24 * 3600 * 1000 * 1000) {
        txt = txt.substring(11, 15);
      } else if (range < 30 * 24 * 3600 * 1000 * 1000) {
        txt = txt.substring(5, 10);
      } else {
        txt = txt.substring(0, 10);
      }
      return Text(txt);
    }

    return Column(
      mainAxisSize: MainAxisSize.max,
      children: [
        //Obx(()=>
        SizedBox(
          height: 150,
          child: Row(children: [
            Expanded(
              flex: 2,
              child: Padding(
                padding: const EdgeInsets.fromLTRB(30, 0, 10, 0),
                child: Column(
                    mainAxisSize: MainAxisSize.max,
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Obx(() =>
                          Text("设备：${controller._selLable.value}", style: ts)),
                      Obx(() => Text("电量：${controller._selEnergy.value}%",
                          style: ts)),
                    ]),
              ),
            ),
            Expanded(
                flex: 2,
                child: Obx(() => CustomPaint(
                      painter: SpeedometerPainter(controller._selTemp.value,
                          name: "温度", minSpeed: -60, maxSpeed: 160),
                      size: const Size(300, 150),
                    ))),
            Expanded(
              flex: 1,
              child:
                  Obx(() => Column(mainAxisSize: MainAxisSize.max, children: [
                        const Spacer(flex: 5),
                        ElevatedButton(
                            onPressed: () {
                              controller._delete(controller._selId.value);
                              controller._validShow();
                            },
                            child: const Text("清除数据")),
                        const Spacer(flex: 1),
                        ElevatedButton(
                            onPressed: () {
                              final data =
                                  controller._allDatas[controller._selId.value];
                              if (data != null) {
                                data.hide = !data.hide;
                                controller._save(data);
                                controller._validShow();
                              }
                            },
                            child: Text(hide.value ? "添加配对" : "取消配对")),
                        const Spacer(flex: 5),
                      ])),
            )
          ]),
        ),
        Expanded(
            child: Obx(() => LineChart(
                  LineChartData(
                      minY: -60,
                      maxY: 150,
                      lineBarsData: [
                        LineChartBarData(
                            dotData: const FlDotData(show: false),
                            spots: controller._his)
                      ],
                      lineTouchData: LineTouchData(
                        touchTooltipData: LineTouchTooltipData(
                            getTooltipItems: (touchedSpots) =>
                                touchedSpots.map((LineBarSpot touchedSpot) {
                                  const textStyle = TextStyle(
                                    color: Color.fromARGB(255, 205, 238, 255),
                                    fontWeight: FontWeight.bold,
                                    fontSize: 14,
                                  );
                                  return LineTooltipItem(
                                      "${touchedSpot.y.toStringAsFixed(2)}℃\n${DateTime.fromMicrosecondsSinceEpoch(touchedSpot.x.toInt()).toLocal().toString().substring(0, 19)}",
                                      textStyle);
                                }).toList()),
                        handleBuiltInTouches: true, // 允许内置触摸处理
                      ),
                      gridData: const FlGridData(
                        show: true,
                        drawHorizontalLine: true,
                        drawVerticalLine: true,
                      ),
                      titlesData: FlTitlesData(
                        show: true,
                        bottomTitles: AxisTitles(
                            sideTitles: SideTitles(
                                showTitles: true,
                                getTitlesWidget: (value, meta) =>
                                    Transform.rotate(
                                      origin: const Offset(-20, -30),
                                      angle: -pi * 45 / 180,
                                      child: makeLable(value),
                                    ))),
                        leftTitles: const AxisTitles(
                            sideTitles: SideTitles(showTitles: false)),
                        rightTitles: const AxisTitles(
                            sideTitles: SideTitles(showTitles: false)),
                        topTitles: const AxisTitles(
                            sideTitles: SideTitles(showTitles: false)),
                      )),
                )))
      ],
    );
  }

  buildBtnBar() {
    List<Widget> btns = [];
    for (var dev in controller._devs) {
      btns.add(
        ElevatedButton(onPressed: () {}, child: Text(dev)),
      );
    }
    return btns;
  }
}

extension SimpleSize on Size {
  Offset get centerPt => center(const Offset(0, 0));
}

class SpeedometerPainter extends CustomPainter {
  final String name;
  final double speed;
  final double minSpeed; // 最大速度，用于计算指针的角度
  final double maxSpeed; // 最大速度，用于计算指针的角度
  Picture? backgroundPicture;
  Size oSize = Size.zero;

  SpeedometerPainter(this.speed,
      {this.name = "", this.minSpeed = 0, this.maxSpeed = 100});

  void peraperBackground(Size size) {
    if (backgroundPicture != null && oSize == size) {
      return;
    }

    oSize = size;
    final recorder = PictureRecorder();
    final canvas = Canvas(recorder);

    // 绘制固定的内容
    final paint = Paint()
      ..color = const Color.fromARGB(255, 248, 246, 246)
      ..style = PaintingStyle.stroke
      ..strokeWidth = 0.5
      ..isAntiAlias = true;

    final double radius = min(size.width / 2, size.height);

    // 绘制半圆
    final rect =
        Rect.fromCircle(center: Offset(radius, radius), radius: radius - 10);
    canvas.drawArc(rect, pi, pi, false, paint); // 绘制从-90度到90度的半圆

    const int num = 60;
    final cpt = Offset(radius, radius); // size.bottomCenter(Offset.zero);
    // 绘制刻度线
    final double outerRadius = radius - 20;
    final double innerRadius = radius - 30;
    const textStyle = TextStyle(fontSize: 12);
    for (var i = 0; i <= num; i++) {
      double startAngle = (-pi / 2 + i * pi / num) + pi / 2;
      double endAngle = (-pi / 2 + i * pi / num) + pi / 2;
      Path path = Path();
      if (i % 10 == 0) {
        double dx = cpt.dx + cos(startAngle) * (innerRadius - 10);
        double dy = cpt.dy - sin(startAngle) * (innerRadius - 10);
        path = path..moveTo(dx, dy);
        double label = i * (maxSpeed - minSpeed) / num + minSpeed;
        var textLayout = TextPainter(
          text: TextSpan(text: label.toStringAsFixed(0), style: textStyle),
          textDirection: TextDirection.ltr,
          textAlign: TextAlign.center,
        );
        textLayout.layout();
        textLayout.paint(
            canvas,
            Offset.fromDirection(startAngle + pi, innerRadius - 20)
                .translate(radius - 8, radius - 10));
      } else {
        path = path
          ..moveTo(cpt.dx + cos(startAngle) * innerRadius,
              cpt.dy - sin(startAngle) * innerRadius);
      }
      path = path
        ..lineTo(cpt.dx + cos(endAngle) * outerRadius,
            cpt.dy - sin(endAngle) * outerRadius);
      canvas.drawPath(path, paint);
    }
    var textLayout = TextPainter(
      text: TextSpan(text: name, style: textStyle),
      textDirection: TextDirection.ltr,
      textAlign: TextAlign.center,
    );
    textLayout.layout();
    textLayout.paint(
        canvas,
        Offset.fromDirection(3 * pi / 2, innerRadius / 2)
            .translate(radius - 10, radius - 10));

    // 结束录制并获取Picture
    backgroundPicture = recorder.endRecording();
  }

  @override
  void paint(Canvas canvas, Size size) {
    peraperBackground(size);
    canvas.drawPicture(backgroundPicture!);
    final double radius = min(size.width / 2, size.height);
    // 绘制指针
    final angle =
        pi - (speed - minSpeed) / (maxSpeed - minSpeed) * pi; // 指针的角度，从-90度开始
    final endX = radius + cos(angle) * (radius - 50);
    final endY = radius - sin(angle) * (radius - 50);
    final startX = radius + cos(angle) * 20;
    final startY = radius - sin(angle) * 20;
    final end = Offset(
        endX.clamp(0.0, radius * 2), endY.clamp(0.0, radius)); // 确保指针不会超出画布
    final start = Offset(
        startX.clamp(0.0, radius * 2), startY.clamp(0.0, radius)); // 确保指针不会超出画布

    final pointerPaint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.fill
      ..strokeWidth = 3.0
      ..isAntiAlias = true;

    const textStyle = TextStyle(fontSize: 12);

    var textLayout = TextPainter(
      text: TextSpan(text: speed.toStringAsFixed(2), style: textStyle),
      textDirection: TextDirection.ltr,
      textAlign: TextAlign.center,
    );
    final cpt = Offset(radius, radius); // size.bottomCenter(Offset.zero);
    textLayout.layout();
    textLayout.paint(canvas, cpt.translate(-10, -10));
    canvas.drawLine(start, end, pointerPaint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    // 如果速度或激活状态改变，则重绘
    if (oldDelegate is SpeedometerPainter) {
      final oldPainter = oldDelegate;
      if (name == oldPainter.name &&
          minSpeed == oldPainter.minSpeed &&
          maxSpeed == oldPainter.maxSpeed) {
        backgroundPicture ??= oldPainter.backgroundPicture;
        oSize = oldPainter.oSize;
      } else {
        backgroundPicture = null;
      }
      if (speed != oldPainter.speed) {
        return true;
      }
    }
    return backgroundPicture == null;
  }
}
